package com.jh.paymentchannel.service;

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import net.sf.json.JSONObject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import cn.jh.common.tools.Base64;
import cn.jh.common.tools.Log;
import cn.jh.common.tools.Tools;
import cn.jh.common.tools.Xml;
import cn.jh.common.tools.http.HttpClient;
import cn.jh.common.tools.rsa.Signatory;

import com.jh.paymentchannel.util.Util;
import com.jh.paymentchannel.util.YiLianKeyConstants;

@Service
public class YiLianTopupRequest implements TopupRequest{
	
	private static final Logger log = LoggerFactory.getLogger(YiLianTopupRequest.class);
	
	@Autowired
	Util util;
	
	
	 /**
    * 【H5页面标准版】的商户订单下单接口，本接口比SDK的下单接口增加了【returnUrl】和【clientIp】参数
    * @param merchantId        商户代码
    * @param merchOrderId      商户订单号
    * @param amount            商户订单金额，单位为元，格式： nnnnnn.nn
    * @param orderDesc         商户订单描述    字符最大128，中文最多40个；参与签名：采用UTF-8编码
    * @param tradeTime         商户订单提交时间，格式：yyyyMMddHHmmss，超过订单超时时间未支付，订单作废；不提交该参数，采用系统的默认时间（从接收订单后超时时间为30分钟）
    * @param expTime           交易超时时间，格式：yyyyMMddHHmmss， 超过订单超时时间未支付，订单作废；不提交该参数，采用系统的默认时间（从接收订单后超时时间为30分钟）
    * @param notifyUrl         异步通知URL
    * @param returnUrl         同步通知URL
    * @param extData           商户保留信息； 通知结果时，原样返回给商户；字符最大128，中文最多40个；参与签名：采用UTF-8编码
    * @param miscData          订单扩展信息   根据不同的行业，传送的信息不一样；参与签名：采用UTF-8编码
    * @param notifyFlag        订单通知标志    0：成功才通知，1：全部通知（成功或失败）  不填默认为“1：全部通知”
    * @param clientIp          针对配置了防钓鱼的商户需要提交，商户服务器通过获取访问ip得到该参数
    * @param mercPriKey        商户签名的私钥
    * @param payecoPubKey      易联签名验证公钥
    * @param payecoUrl         易联服务器URL地址，只需要填写域名部分
    * @param retXml            通讯返回数据；当不是通讯错误时，该对象返回数据
    * @return  处理状态码： 0000 : 处理成功， 其他： 处理失败
    * @throws Exception        E101:通讯失败； E102：签名验证失败；  E103：签名失败；
    */
   public  String MerchantOrderH5(String merchantId, String merchOrderId,
           String amount, String orderDesc, String tradeTime, String expTime,
           String notifyUrl, String returnUrl, String extData,
           String miscData, String notifyFlag, String clientIp, String mercPriKey,
           String payecoPubKey, String payecoUrl, Xml retXml, String bankcard) throws Exception {
		
	   //交易参数
		String tradeCode = "PayOrder";
		String version = YiLianKeyConstants.COMM_INTF_VERSION;
		
		//进行数据签名  
		String signData = "Version="+version+"&MerchantId=" + merchantId + "&MerchOrderId=" + merchOrderId 
				+ "&Amount=" + amount + "&OrderDesc=" + orderDesc + "&TradeTime=" + tradeTime + "&ExpTime="
				+ expTime + "&NotifyUrl=" + notifyUrl + "&ReturnUrl=" + returnUrl + "&ExtData=" + extData
				+ "&MiscData=" + miscData + "&NotifyFlag=" + notifyFlag + "&ClientIp=" + clientIp;
		
		// 私钥签名
		Log.println("PrivateKey = " + mercPriKey);
		Log.println("SignData = " + signData);
		String sign = Signatory.sign(mercPriKey, signData, YiLianKeyConstants.PAYECO_DATA_ENCODE);
		if(Tools.isStrEmpty(sign)){
			throw new Exception("E103");
		}
		Log.println("sign=" + sign);

		//提交参数包含中文的需要做base64转码
		String orderDesc64 = Base64.encodeBytes(orderDesc.getBytes(YiLianKeyConstants.PAYECO_DATA_ENCODE));
		String extData64 = Base64.encodeBytes(extData.getBytes(YiLianKeyConstants.PAYECO_DATA_ENCODE));
		String miscData64 = Base64.encodeBytes(miscData.getBytes(YiLianKeyConstants.PAYECO_DATA_ENCODE));
		//通知地址做URLEncoder处理
		String notifyUrlEn = URLEncoder.encode(notifyUrl, YiLianKeyConstants.PAYECO_DATA_ENCODE);
		String returnUrlEn = URLEncoder.encode(returnUrl, YiLianKeyConstants.PAYECO_DATA_ENCODE);
		
		String data64 = "Version="+version+"&MerchantId=" + merchantId + "&MerchOrderId=" + merchOrderId 
                   + "&Amount=" + amount + "&OrderDesc=" + orderDesc64 + "&TradeTime=" + tradeTime + "&ExpTime="
                   + expTime + "&NotifyUrl=" + notifyUrlEn + "&ReturnUrl=" + returnUrlEn + "&ExtData=" + extData64
                   + "&MiscData=" + miscData64 + "&NotifyFlag=" + notifyFlag + "&ClientIp=" + clientIp;

		//通讯报文
		String url= payecoUrl + "/ppi/merchant/itf.do"; //下订单URL
		data64 = "TradeCode=" + tradeCode + "&" + data64 + "&Sign=" + sign;
		HttpClient httpClient = new HttpClient();
		Log.println("url = " + url + "?" + data64);
		String retStr = httpClient.send(url, data64, YiLianKeyConstants.PAYECO_DATA_ENCODE, YiLianKeyConstants.PAYECO_DATA_ENCODE,
				YiLianKeyConstants.CONNECT_TIME_OUT, YiLianKeyConstants.RESPONSE_TIME_OUT);
		Log.println("retStr="+retStr);
		if(Tools.isStrEmpty(retStr)){
			throw new Exception("E101");
		}

		//返回数据的返回码判断
		retXml.setXmlData(retStr);
		String retCode = Tools.getXMLValue(retStr, "retCode");
		retXml.setRetCode(retCode);
		retXml.setRetMsg(Tools.getXMLValue(retStr, "retMsg"));
		if(!"0000".equals(retCode)){
			return retCode;
		}
		//获取返回数据
		String retVer = Tools.getXMLValue(retStr, "Version");
		String retMerchantId = Tools.getXMLValue(retStr, "MerchantId");
		String retMerchOrderId = Tools.getXMLValue(retStr, "MerchOrderId");
		String retAmount = Tools.getXMLValue(retStr, "Amount");
		String retTradeTime = Tools.getXMLValue(retStr, "TradeTime");
		String retOrderId = Tools.getXMLValue(retStr, "OrderId");
		String retVerifyTime = Tools.getXMLValue(retStr, "VerifyTime");
		String retSign = Tools.getXMLValue(retStr, "Sign");
		
		//设置返回数据
		retXml.setTradeCode(tradeCode);
		retXml.setVersion(retVer);
		retXml.setMerchantId(retMerchantId);
		retXml.setMerchOrderId(retMerchOrderId);
		retXml.setAmount(retAmount);
		retXml.setTradeTime(tradeTime);
		retXml.setOrderId(retOrderId);
		retXml.setVerifyTime(retVerifyTime);
		retXml.setSign(retSign);
		
		//验证签名的字符串
		String backSign = "Version="+retVer+"&MerchantId=" + retMerchantId + "&MerchOrderId=" + retMerchOrderId 
				+ "&Amount=" + retAmount + "&TradeTime=" + retTradeTime + "&OrderId=" + retOrderId + "&VerifyTime=" + retVerifyTime;
		//验证签名
		retSign = retSign.replaceAll(" ", "+");
		boolean b = Signatory.verify(payecoPubKey, backSign, retSign, YiLianKeyConstants.PAYECO_DATA_ENCODE);
		Log.println("PublicKey=" + payecoPubKey);
		Log.println("data=" + backSign);
		Log.println("Sign=" + retSign);
		Log.println("验证结果=" + b);
		if(!b){
			throw new Exception("E102");
		}
		return retCode;
	}
	
	
   /**
    * 【H5页面标准版】的生成订单支付重定向地址
	 * @param payecoUrl		：	易联服务器URL地址，只需要填写域名部分 
    * @param retXml 下单成功后的通讯返回数据
    * @return
    */
   public  String getPayInitRedirectUrl(String payecoUrl, Xml retXml) {
       String tradeId = "h5Init";
       String version = YiLianKeyConstants.COMM_INTF_VERSION;
       String merchantId = retXml.getMerchantId();         //商户代码
       String merchOrderId = retXml.getMerchOrderId();     //商户订单号
       String amount = retXml.getAmount();                 //商户订单金额，单位为元，格式： nnnnnn.nn
       String tradeTime = retXml.getTradeTime();           //商户订单提交时间
       String orderId = retXml.getOrderId();               //易联订单号
       String verifyTime = retXml.getVerifyTime();         //验证时间戳
       String sign = retXml.getSign();                     //签名,下单时返回的签名
       
       String datas = "Version=" + version + "&MerchantId=" + merchantId
               + "&MerchOrderId=" + merchOrderId + "&Amount=" + amount
               + "&TradeTime=" + tradeTime + "&OrderId=" + orderId
               + "&VerifyTime=" + verifyTime + "&Sign=" + sign;
      
       String redirectUrl = payecoUrl + "/ppi/h5/plugin/itf.do?tradeId=" + tradeId + "&" + datas;
       
       Log.println("redirectUrl=" + redirectUrl);
       
       return redirectUrl;
   }	
	    
  


	@Override
	public Map<String, String> topupRequest(HttpServletRequest request,  String amount, 
			String ordercode, String orderDesc, String extData, String notifyUrl, String returnUrl, String channelparam) throws UnsupportedEncodingException {

		
		    /**根据订单号获取相应的订单*/
			RestTemplate restTemplate=new RestTemplate();
			URI uri = util.getServiceUrl("transactionclear", "error url request!");
			String url = uri.toString() + "/v1.0/transactionclear/payment/query/ordercode";
			MultiValueMap<String, String> requestEntity  = new LinkedMultiValueMap<String, String>();
			requestEntity.add("order_code", ordercode);
			String result = restTemplate.postForObject(url, requestEntity, String.class);
			log.info("RESULT================"+result);
			JSONObject jsonObject =  JSONObject.fromObject(result);
			JSONObject resultObj  =  jsonObject.getJSONObject("result");
			String bankcard       =  resultObj.getString("bankcard");
				
			//设置订单数据；  商户在实际使用情况会有部分数据为手机端提交的数据
			String clientIp = request.getRemoteAddr();//商户用户访问IP地址

			if(bankcard == null || bankcard.equalsIgnoreCase("")){
				return null;
			}
			
			//以下扩展参数是按互联网金融行业填写的；其他行业请参考接口文件说明进行填写
			String miscData = "|||||"+bankcard+"|||0|";
			
		    //下订单处理自动设置的参数
			String merchOrderId = ordercode;  //订单号；本例子按时间产生； 商户请按自己的规则产生
			String merchantId = YiLianKeyConstants.MER_ID;
				
			String tradeTime =  Tools.getSysTime();
			String expTime = ""; //采用系统默认的订单有效时间
			String notifyFlag = "0";

			String priKey = YiLianKeyConstants.MER_PRIVATE_KEY;
			String pubKey = YiLianKeyConstants.YILIAN_PUB_KEY_PRIVATE;
			
			String payecoUrl = YiLianKeyConstants.PAYECO_URL;
				// 调用下单接口
				Xml retXml = new Xml();
				String retMsgJson = "for HTTP1.1";
				boolean bOK = true;

				try {
		            Log.setLogFlag(true);
		            Log.println("---交易： 商户下订单(H5版本)-------------------------");
		            String ret = MerchantOrderH5(merchantId, merchOrderId, amount, orderDesc, 
		            		tradeTime, expTime, notifyUrl, returnUrl, extData, miscData, notifyFlag, clientIp, 
		            		priKey, pubKey, payecoUrl, retXml, bankcard);
		            if (!"0000".equals(ret)) {
		                bOK = false;
		                retMsgJson = "{\"RetCode\":\"" + ret  + "\",\"RetMsg\":\"下订单接口返回错误!\"}";
		            }
		        } catch (Exception e) {
		            bOK = false;
		            String errCode = e.getMessage();
		            if ("E101".equalsIgnoreCase(errCode)) {
		                retMsgJson = "{\"RetCode\":\"E101\",\"RetMsg\":\"下订单接口无返回数据!\"}";
		            } else if ("E102".equalsIgnoreCase(errCode)) {
		                retMsgJson = "{\"RetCode\":\"E102\",\"RetMsg\":\"验证签名失败!\"}";
		            } else if ("E103".equalsIgnoreCase(errCode)) {
		                retMsgJson = "{\"RetCode\":\"E103\",\"RetMsg\":\"进行订单签名失败!\"}";
		            } else {
		                retMsgJson = "{\"RetCode\":\"E100\",\"RetMsg\":\"下订单通讯失败!\"}";
		            }
		        }
				
				Map<String, String> map = new HashMap<String, String>();	
				
		        //重定向到订单支付
		        if(bOK){
		        	//根据返回的参数组织向易联支付平台提交支付申请的URL
		            String redirectUrl = getPayInitRedirectUrl(payecoUrl, retXml);
		            Log.println("PayURL : "+redirectUrl);
		            
		            //针对【支付申请URL】，可以采用直接sendRedirect转跳的方式；也可以采用页面确认后再转跳的方式；
		            //商户根据自己的业务逻辑选择，建议在正式使用时采用sendRedirect转跳方式；
		            //response.sendRedirect(redirectUrl); //sendRedirect转跳方式
		            
		            //--页面确认后再转跳的方式
		            /*retMsgJson = "<html><head><title>易联支付H5测试-支付请求</title></head><body>支付请求URL: "+redirectUrl+"<br/>"
		            			 +" <a href=\""+redirectUrl+"\">立即支付</a></body></html>";
		            PrintWriter out = response.getWriter();
		            out.println(retMsgJson);
		            out.close(); // for HTTP1.1 
		            
*/		        
		            map.put("resp_code", "success");
		            map.put("channel_type", "quick");
		            map.put("redirect_url", redirectUrl);
		            return map;
		        
		        }else{
		            //返回数据
		          /*  PrintWriter out = response.getWriter();
		            out.println(retMsgJson);
		            out.close(); // for HTTP1.1 
*/		        
		        	map.put("resp_code", "failed");
		        	return map;
		        }
	}


   
	
}
